Dubbo源码分析3-Dubbo 服务导出1

计划阅读调试下Dubbo的源码,结合官方源码分析Dubbo,自身再分析总结

本文对应的Dubbo 服务导出

介绍

Dubbo 导出服务分为三个部分:

  • 前置工作:检查参数,组装默认值,组装URL
  • 导出服务:导出服务到本地(InJvmExporter),和导出服务到远程
  • 注册服务:向注册中心注册服务用于服务发现

源码分析

前置工作

过程起始于ServiceBean#onApplicationEventServiceBean用于连接Dubbo和Spring。Dubbo在收到事件通知之后就会导出服务。

1
2
3
4
5
6
7
8
9
10
11
@Override
// 事件类型为Spring的上下文刷新事件
public void onApplicationEvent(ContextRefreshedEvent event) {
// 是否延迟导出 && 是否已经导出 && 是否已取消导出
if (isDelay() && !isExported() && !isUnexported()) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
export();
}
}

其中isDelay方法,含义与字面意义是相反的

1
2
3
4
5
6
7
8
private boolean isDelay() {
Integer delay = getDelay();
ProviderConfig provider = getProvider();
if (delay == null && provider != null) {
delay = provider.getDelay();
}
return supportedApplicationListener && (delay == null || delay == -1);
}

例如如果provider中配置了delay为10,则返回false,而未配置delay则为true

之后进入export方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public synchronized void export() {
if (provider != null) {
// 获取是否需要暴露
if (export == null) {
export = provider.getExport();
}
// 是否延迟暴露
if (delay == null) {
delay = provider.getDelay();
}
}
// 如果不暴露则终止
if (export != null && !export) {
return;
}

if (delay != null && delay > 0) {
delayExportExecutor.schedule(new Runnable() {
@Override
public void run() {
doExport();
}
}, delay, TimeUnit.MILLISECONDS);
} else {
doExport();
}
}

上面代码逻辑比较简单,继续进入doExport方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
protected synchronized void doExport() {
// 已经取消暴露
if (unexported) {
throw new IllegalStateException("Already unexported!");
}

// 已经暴露
if (exported) {
return;
}

exported = true;

// 校验接口名称是否为空
if (interfaceName == null || interfaceName.length() == 0) {
throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
}

// 检查默认值
checkDefault();
// 如果provider不为空,则从中获取应用信息,模块配置、注册地址等信息对实例赋值
if (provider != null) {
if (application == null) {
application = provider.getApplication();
}
if (module == null) {
module = provider.getModule();
}
if (registries == null) {
registries = provider.getRegistries();
}
if (monitor == null) {
monitor = provider.getMonitor();
}
if (protocols == null) {
protocols = provider.getProtocols();
}
}
if (module != null) {
if (registries == null) {
registries = module.getRegistries();
}
if (monitor == null) {
monitor = module.getMonitor();
}
}
if (application != null) {
if (registries == null) {
registries = application.getRegistries();
}
if (monitor == null) {
monitor = application.getMonitor();
}
}
// ......

// 判断是否是通用服务,通用服务即不需要服务提供接口
if (ref instanceof GenericService) {
interfaceClass = GenericService.class;
if (StringUtils.isEmpty(generic)) {
generic = Boolean.TRUE.toString();
}
} else {
try {
// 获取接口
interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
.getContextClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
// 校验接口和方法是否一致
checkInterfaceAndMethods(interfaceClass, methods);
// 检查ref是否非空以及是否是接口的实现类
checkRef();
generic = Boolean.FALSE.toString();
}
// local 与 stub 为本地存根逻辑
if (local != null) {
if ("true".equals(local)) {
local = interfaceName + "Local";
}
Class<?> localClass;
try {
// 加载本地存根类,如果local字段为为true则默认加载xxxxLocal类
localClass = ClassHelper.forNameWithThreadContextClassLoader(local);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
if (!interfaceClass.isAssignableFrom(localClass)) {
throw new IllegalStateException("The local implementation class " + localClass.getName() + " not implement interface " + interfaceName);
}
}
if (stub != null) {
// 逻辑同local部分
}
// 校验application等
checkApplication();
checkRegistry();
checkProtocol();

// 对当前对象的各个对象检测是否为空,为空的话从系统参数中获取后新建
appendProperties(this);

// 校验本地存根类以及本地伪装类
checkStubAndMock(interfaceClass);
if (path == null || path.length() == 0) {
path = interfaceName;
}
// 暴露Urls
doExportUrls();
// 创建提供者模型,记录服务名称与配置,以及实现类关系,其中uniqueServiceName是由group+接口名称+版本组成
ProviderModel providerModel = new ProviderModel(getUniqueServiceName(), this, ref);
// 存入缓存
ApplicationModel.initProviderModel(getUniqueServiceName(), providerModel);
}

上面的逻辑比较复杂,主要做了如下事情:

  • 对<dubbo:service interface=””…> 合理性校验

  • 对application module registers等配置类赋值以及校验

  • 对ref合理性进行校验

  • 对stub本地存根以及mock本地伪装类进行合理性校验

  • 暴露Urls

后续介绍核心部分doExportUrls